package cn.donting.jme.utile.savable;

import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.util.IntMap;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.*;
import java.util.stream.Collectors;

public class TxtOutputCapsule implements OutputCapsule {

    final HashMap<String, String> content;
    final TxtJmeExporter txtJmeExporter;
    final int id;
    final Savable savable;

    private static final String tab = "   ";

    public TxtOutputCapsule(TxtJmeExporter txtJmeExporter, Savable savable, int id) {
        this.content = new HashMap<>();
        this.txtJmeExporter = txtJmeExporter;
        this.id = id;
        this.savable = savable;
    }

    private String getValue(byte value) {
        return value + "";
    }

    private String getValue(byte[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(byte[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(tab);
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append("\n");
            }
        }
//        stringBuilder.append("[");
        return stringBuilder.toString();
    }

    private String getValue(String value) {
        return value + "";
    }

    private String getValue(String[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(String[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(int value) {
        return value + "";
    }

    private String getValue(int[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(int[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(float value) {
        return value + "";
    }

    private String getValue(float[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(float[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(double value) {
        return value + "";
    }

    private String getValue(double[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(double[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(boolean value) {
        return value + "";
    }

    private String getValue(boolean[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(boolean[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }


    private String getValue(long value) {
        return value + "";
    }

    private String getValue(long[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(long[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(short value) {
        return value + "";
    }

    private String getValue(short[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(short[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }


    private String getValue(Savable value) {
        TxtOutputCapsule capsule = txtJmeExporter.getCapsule(value);
        return "#" + capsule.id;
    }

    private String getValue(Savable[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(Savable[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(BitSet value) {
        byte[] byteArray = value.toByteArray();
        return getValue(byteArray);
    }

    private String getValue(ArrayList<Savable> value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < value.size(); i++) {
            stringBuilder.append(getValue(value.get(i)));
            if (i != value.size() - 1) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(ArrayList<Savable>[] value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            if (i != value.length - 1) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    private String getValue(ArrayList<Savable>[][] value) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < value.length; i++) {
            stringBuilder.append(getValue(value[i]));
            stringBuilder.append(",");
        }
        return stringBuilder.toString();
    }

    private String getValue(FloatBuffer value) {
        StringBuilder stringBuilder = new StringBuilder();
        value.rewind();
        int length = value.limit();
        stringBuilder.append("[");
        for (int x = 0; x < length; x++) {
            stringBuilder.append(getValue(value.get()));
            if(x!=length-1){
                stringBuilder.append(",");
            }
        }
        value.rewind();
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(ByteBuffer value) {
        StringBuilder stringBuilder = new StringBuilder();
        value.rewind();
        int length = value.limit();
        stringBuilder.append("[");
        for (int x = 0; x < length; x++) {
            stringBuilder.append(getValue(value.get()));
        }
        value.rewind();
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(IntBuffer value) {
        StringBuilder stringBuilder = new StringBuilder();
        value.rewind();
        int length = value.limit();
        stringBuilder.append("[");
        for (int x = 0; x < length; x++) {
            stringBuilder.append(getValue(value.get()));
            stringBuilder.append(",");
        }
        value.rewind();
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(ShortBuffer value) {
        StringBuilder stringBuilder = new StringBuilder();
        value.rewind();
        int length = value.limit();
        stringBuilder.append("[");
        for (int x = 0; x < length; x++) {
            stringBuilder.append(getValue(value.get()));
            stringBuilder.append(",");
        }
        value.rewind();
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    private String getValue(Enum value) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(value.getClass().getName()).append("#").append(value.name());
        return stringBuilder.toString();
    }

    private String getValueFloatBuffer(ArrayList<FloatBuffer> value) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < value.size(); i++) {
            stringBuilder.append(getValue(value.get(i)));
        }
        return stringBuilder.toString();
    }

    private String getValueByteBuffer(ArrayList<ByteBuffer> value) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < value.size(); i++) {
            stringBuilder.append(getValue(value.get(i)));
        }
        return stringBuilder.toString();
    }


    private String getValueSavableMap(Map<? extends Savable, ? extends Savable> map) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        List<? extends Savable> collect = map.keySet().stream().sorted((Comparator<Savable>) (o1, o2) -> txtJmeExporter.getCapsule(o1).id - txtJmeExporter.getCapsule(o2).id).collect(Collectors.toList());
        for (int i = 0; i < collect.size(); i++) {
            Savable key = collect.get(i);
            Savable value = map.get(key);
            stringBuilder.append(getValue(key)).append(":").append(getValue(value));
            if (i != collect.size() - 1) {
                stringBuilder.append("\n");
            }
        }
        return stringBuilder.toString();
    }

    private String getValueStringMap(Map<String, ? extends Savable> map) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        List<String> collect = map.keySet().stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
        for (int i = 0; i < collect.size(); i++) {
            String key = collect.get(i);
            Savable value = map.get(key);
            stringBuilder.append(getValue(key)).append(":").append(getValue(value));
            if (i != collect.size() - 1) {
                stringBuilder.append("\n");
            }
        }
        return stringBuilder.toString();
    }

    private String getValueIntMap(IntMap<? extends Savable> map) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n").append(tab);
        List<Integer> keys = new ArrayList<>(map.size());
        for (IntMap.Entry<? extends Savable> entry : map) {
            int key = entry.getKey();
            keys.add(key);
        }
        keys.sort(Integer::compareTo);
        for (int i = 0; i < keys.size(); i++) {
            int key = keys.get(i);
            Savable value = map.get(key);
            stringBuilder.append(getValue(key)).append(":").append(getValue(value));
            if (i != keys.size() - 1) {
                stringBuilder.append("\n");
            }
        }
        return stringBuilder.toString();
    }


    @Override
    public void write(byte value, String name, byte defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(byte[] value, String name, byte[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(byte[][] value, String name, byte[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(int value, String name, int defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(int[] value, String name, int[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(int[][] value, String name, int[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(float value, String name, float defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(float[] value, String name, float[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(float[][] value, String name, float[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(double value, String name, double defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(double[] value, String name, double[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(double[][] value, String name, double[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(long value, String name, long defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(long[] value, String name, long[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(long[][] value, String name, long[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(short value, String name, short defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(short[] value, String name, short[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(short[][] value, String name, short[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(boolean value, String name, boolean defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(boolean[] value, String name, boolean[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(boolean[][] value, String name, boolean[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(String value, String name, String defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(String[] value, String name, String[] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(String[][] value, String name, String[][] defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(BitSet value, String name, BitSet defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(Savable object, String name, Savable defVal) throws IOException {
        if (object == defVal)
            return;
        String valueStr = getValue(object);
        content.put(name, valueStr);
    }

    @Override
    public void write(Savable[] objects, String name, Savable[] defVal) throws IOException {
        if (objects == defVal)
            return;
        String valueStr = getValue(objects);
        content.put(name, valueStr);
    }

    @Override
    public void write(Savable[][] objects, String name, Savable[][] defVal) throws IOException {
        if (objects == defVal)
            return;
        String valueStr = getValue(objects);
        content.put(name, valueStr);
    }

    @Override
    public void writeSavableArrayList(ArrayList array, String name, ArrayList defVal) throws IOException {
        if (array == defVal)
            return;
        String valueStr = getValue(array);
        content.put(name, valueStr);
    }

    @Override
    public void writeSavableArrayListArray(ArrayList[] array, String name, ArrayList[] defVal) throws IOException {
        if (array == defVal)
            return;
        String valueStr = getValue(array);
        content.put(name, valueStr);
    }

    @Override
    public void writeSavableArrayListArray2D(ArrayList[][] array, String name, ArrayList[][] defVal) throws IOException {
        if (array == defVal)
            return;
        String valueStr = getValue(array);
        content.put(name, valueStr);
    }

    @Override
    public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array, String name, ArrayList<FloatBuffer> defVal) throws IOException {
        if (array == defVal)
            return;
        String valueStr = getValueFloatBuffer(array);
        content.put(name, valueStr);
    }

    @Override
    public void writeByteBufferArrayList(ArrayList<ByteBuffer> array, String name, ArrayList<ByteBuffer> defVal) throws IOException {
        if (array == defVal)
            return;
        String valueStr = getValueByteBuffer(array);
        content.put(name, valueStr);
    }

    @Override
    public void writeSavableMap(Map<? extends Savable, ? extends Savable> map, String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException {
        if (map == defVal)
            return;
        String valueStr = getValueSavableMap(map);
        content.put(name, valueStr);
    }


    @Override
    public void writeStringSavableMap(Map<String, ? extends Savable> map, String name, Map<String, ? extends Savable> defVal) throws IOException {
        if (map == defVal)
            return;
        String valueStr = getValueStringMap(map);
        content.put(name, valueStr);
    }

    @Override
    public void writeIntSavableMap(IntMap<? extends Savable> map, String name, IntMap<? extends Savable> defVal) throws IOException {
        if (map == defVal)
            return;
        String valueStr = getValueIntMap(map);
        content.put(name, valueStr);
    }

    @Override
    public void write(FloatBuffer value, String name, FloatBuffer defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(IntBuffer value, String name, IntBuffer defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(ByteBuffer value, String name, ByteBuffer defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(ShortBuffer value, String name, ShortBuffer defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }

    @Override
    public void write(Enum value, String name, Enum defVal) throws IOException {
        if (value == defVal)
            return;
        String valueStr = getValue(value);
        content.put(name, valueStr);
    }


    void toTxt(StringBuilder out) {
        List<String> kes = content.keySet().stream().sorted(String::compareTo).collect(Collectors.toList());
        out.append("[#").append(id).append("@").append(savable.getClass().getName()).append("]").append("\n");
        for (String key : kes) {
            String c = content.get(key);
            out.append("#").append(key).append(":");
            out.append(c);
            out.append("\n");
        }
    }
}
